#![windows_subsystem = "windows"]

extern crate native_windows_derive as nwd;
extern crate native_windows_gui as nwg;

use anyhow::bail;
use nwd::NwgUi;
use nwg::NativeUi;
use rand::distributions::{Alphanumeric, DistString};
use serde_json::Value;
use std::io::Read;
use std::{cell::RefCell, fs::File, io::Write};


static EMBEDDED_IMAGE1: &[u8] = include_bytes!(".\\..\\images\\gzh.png");
static EMBEDDED_IMAGE2: &[u8] = include_bytes!(".\\..\\images\\wander.jpg");

#[derive(Default, NwgUi)]
pub struct MainApp {
    #[nwg_control(size: (400, 590), position: (300, 300), title: "小工具-生成小程序码", flags: "WINDOW|VISIBLE")]
    #[nwg_events( OnWindowClose: [MainApp::exit],OnInit:[MainApp::load_data] )]
    window: nwg::Window,

    #[nwg_resource]
    decoder: nwg::ImageDecoder,

    #[nwg_resource(family: "Arial", size: 16)]
    arial16: nwg::Font,

    #[nwg_resource(family: "Arial", size: 14)]
    arial14: nwg::Font,

    #[nwg_layout(parent: window)]
    layout: nwg::GridLayout,

    #[nwg_control()]
    #[nwg_layout_item(layout: layout, col: 0, row: 0, col_span: 4, row_span: 2)]
    img_gzh: nwg::ImageFrame,

    #[nwg_control()]
    #[nwg_layout_item(layout: layout, col: 4, row: 0, col_span: 2, row_span: 2)]
    img_wander: nwg::ImageFrame,

    #[nwg_control(text: "小程序的账号信息", font: Some(&data.arial16))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:6, row: 2)]
    lbl_mp_desc1: nwg::Label,

    #[nwg_control(text: "请输入APPID：", font: Some(&data.arial14))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:2, row: 3)]
    lbl_appid: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 10), focus: true)]
    #[nwg_layout_item(layout: layout, col: 2,col_span:4, row: 3)]
    edt_appid: nwg::TextInput,

    #[nwg_control(text: "请输入APPSECRET：", font: Some(&data.arial14))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:2, row: 4)]
    lbl_appsecret: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 10))]
    #[nwg_layout_item(layout: layout, col: 2,col_span:4, row: 4)]
    edt_appsecret: nwg::TextInput,

    #[nwg_control(text: "受限制小程序码，请使用接口1", font: Some(&data.arial16))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:6, row: 5)]
    lbl_qrcode_desc1: nwg::Label,

    #[nwg_control(text: "请输入页面路径：", font: Some(&data.arial14))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:2, row: 6)]
    lbl_path: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 10))]
    #[nwg_layout_item(layout: layout, col: 2,col_span:4, row: 6)]
    edt_path: nwg::TextInput,

    #[nwg_control(text: "不受限制小程序码，请使用接口2", font: Some(&data.arial16))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:6, row: 7)]
    lbl_qrcode_desc2: nwg::Label,

    #[nwg_control(text: "请输入页面：", font: Some(&data.arial14))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:2, row: 8)]
    lbl_page: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 10))]
    #[nwg_layout_item(layout: layout, col: 2,col_span:4, row: 8)]
    edt_page: nwg::TextInput,

    #[nwg_control(text: "请输入场景值：", font: Some(&data.arial14))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:2, row: 9)]
    lbl_scene: nwg::Label,

    #[nwg_control(text: "", size: (280, 35), position: (10, 10))]
    #[nwg_layout_item(layout: layout, col: 2,col_span:4, row: 9)]
    edt_scene: nwg::TextInput,

    #[nwg_control(text: "获取TOKEN", size: (280, 70), position: (10, 50))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:2, row: 10)]
    #[nwg_events( OnButtonClick: [MainApp::get_token] )]
    btn_gettoken: nwg::Button,

    #[nwg_control(text: "接口1", size: (280, 70), position: (10, 50), enabled:false)]
    #[nwg_layout_item(layout: layout, col: 2,col_span:2, row: 10)]
    #[nwg_events( OnButtonClick: [MainApp::get_qrcode1] )]
    btn_qrcode1: nwg::Button,

    #[nwg_control(text: "接口2", size: (280, 70), position: (10, 50), enabled:false)]
    #[nwg_layout_item(layout: layout, col: 4,col_span:2, row: 10)]
    #[nwg_events( OnButtonClick: [MainApp::get_qrcode2] )]
    btn_qrcode2: nwg::Button,

    #[nwg_control(text: "使用说明，请先获取token，然后再生成小程序码，\n生成的小程序码存放在output目录下。", font: Some(&data.arial14))]
    #[nwg_layout_item(layout: layout, col: 0,col_span:6, row: 11)]
    lbl_desc: nwg::Label,

    token: RefCell<String>,
}

impl MainApp {
    fn load_data(&self) {
        let gimg = &self.img_gzh;
        println!("1-{:?}", gimg.size());
        let filedata = self.decoder.from_stream(EMBEDDED_IMAGE1).unwrap();
        // let filedata = self.decoder.from_filename("./images/gzh.png").unwrap();
        let framedata = filedata.frame(0).unwrap();
        let ndata = self.decoder.resize_image(&framedata, [241, 88]).unwrap();
        let nbm = ndata.as_bitmap().unwrap();
        gimg.set_bitmap(Some(&nbm));

        let wimg = &self.img_wander;
        println!("1-{:?}", wimg.size());
        let wdata = self.decoder.from_stream(EMBEDDED_IMAGE2).unwrap();
        // let wdata = self.decoder.from_filename("./images/wander.jpg").unwrap();
        let wframedata = wdata.frame(0).unwrap();
        let wndata = self.decoder.resize_image(&wframedata, [88, 88]).unwrap();
        let wnbm = wndata.as_bitmap().unwrap();
        wimg.set_bitmap(Some(&wnbm));
    }

    fn get_token(&self) {
        let appid = self.edt_appid.text();
        if appid == "" {
            nwg::modal_error_message(&self.window, "提示", "APPID为空");
            return;
        }
        let appsecret = self.edt_appsecret.text();
        if appsecret == "" {
            nwg::modal_error_message(&self.window, "提示", "APPSECERT为空");
            return;
        }

        let token = get_token(&appid, &appsecret);
        match token {
            Ok(token) => {
                println!("token {}", token);
                self.btn_gettoken.set_enabled(false);
                self.btn_qrcode1.set_enabled(true);
                self.btn_qrcode2.set_enabled(true);
                let mut wtoken = self.token.borrow_mut();
                *wtoken = token;
                nwg::modal_info_message(&self.window, "提示", "获取成功");
            }
            Err(e) => {
                let estr = e.to_string();
                nwg::modal_error_message(&self.window, "提示", &estr);
            }
        }
    }
    // 受限制小程序码
    fn get_qrcode1(&self) {
        let ep = self.edt_path.text();
        if ep == "" {
            nwg::modal_error_message(&self.window, "提示", "页面路径为空");
            return;
        }
        let token = self.token.borrow();
        if token.eq("") {
            nwg::modal_error_message(&self.window, "提示", "token无效");
            return;
        }
        match get_mp_qrcode1(&token, &ep) {
            Ok(data) => {
                let rand_str = get_rand_str(6);
                let pf = format!("output/mp1-{}.png", rand_str);
                // 以只写模式打开文件，返回 `io::Result<File>`
                let mut file = match File::create(&pf) {
                    Err(why) => panic!("couldn't create {}: {:?}", pf, why),
                    Ok(file) => file,
                };

                match file.write_all(&data) {
                    Err(why) => {
                        panic!("couldn't write to {}: {:?}", pf, why)
                    }
                    Ok(_) => println!("successfully wrote to {}", pf),
                }

                nwg::modal_info_message(&self.window, "提示", "生成成功");
            }
            Err(e) => {
                let estr = e.to_string();
                nwg::modal_error_message(&self.window, "提示", &estr);
            }
        }
    }
    // 不受限制的小程序码
    fn get_qrcode2(&self) {
        let page = self.edt_page.text();
        if page == "" {
            nwg::modal_error_message(&self.window, "提示", "页面为空");
            return;
        }
        let scene = self.edt_scene.text();
        if scene == "" {
            nwg::modal_error_message(&self.window, "提示", "场景值为空");
            return;
        }

        let token = self.token.borrow();
        if token.eq("") {
            nwg::modal_error_message(&self.window, "提示", "token无效");
            return;
        }
        match get_mp_qrcode2(&token, &page, &scene) {
            Ok(data) => {
                let rand_str = get_rand_str(6);
                let pf = format!("output/mp2-{}.png", rand_str);
                // 以只写模式打开文件，返回 `io::Result<File>`
                let mut file = match File::create(&pf) {
                    Err(why) => panic!("couldn't create {}: {:?}", pf, why),
                    Ok(file) => file,
                };

                match file.write_all(&data) {
                    Err(why) => {
                        panic!("couldn't write to {}: {:?}", pf, why)
                    }
                    Ok(_) => println!("successfully wrote to {}", pf),
                }

                nwg::modal_info_message(&self.window, "提示", "生成成功");
            }
            Err(e) => {
                let estr = e.to_string();
                nwg::modal_error_message(&self.window, "提示", &estr);
            }
        }
    }

    fn exit(&self) {
        nwg::stop_thread_dispatch();
    }
}

fn main() {
    nwg::init().expect("Failed to init Native Windows GUI");
    nwg::Font::set_global_family("Segoe UI").expect("Failed to set default font");

    let _app = MainApp::build_ui(Default::default()).expect("Failed to build UI");

    nwg::dispatch_thread_events();
}

fn get_token(appid: &str, appsecret: &str) -> anyhow::Result<String> {
    let url = format!(
        "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}",
        appid, appsecret
    );

    let json_data: serde_json::Value = ureq::get(&url).call()?.into_json()?;
    println!("json ,{:?}", json_data);
    let access_token = &json_data["access_token"];
    if access_token == &Value::Null {
        let err_msg = format!(
            "get access token error,error info is {{code:{} msg:{}}}",
            json_data["errcode"], json_data["errmsg"]
        );
        bail!(err_msg)
    }
    let token = access_token.as_str().unwrap();
    Ok(token.to_string())
}

// 受限制的小程序码，但是可以根很长的参数。
fn get_mp_qrcode1(token: &str, path: &str) -> anyhow::Result<Vec<u8>> {
    let url = format!(
        "https://api.weixin.qq.com/wxa/getwxacode?access_token={}",
        token
    );
    let resp = ureq::post(&url).send_json(serde_json::json! ({"path":path}))?;
    let len: usize = resp.header("Content-Length").unwrap().parse()?;
    let mut bytes: Vec<u8> = Vec::with_capacity(len);
    resp.into_reader()
        .take(10_000_000)
        .read_to_end(&mut bytes)?;
    Ok(bytes)
}

// 需要场景值，不受限制
fn get_mp_qrcode2(token: &str, page: &str, scene: &str) -> anyhow::Result<Vec<u8>> {
    let url = format!(
        "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token={}",
        token
    );
    let resp = ureq::post(&url).send_json(serde_json::json! ({"page":page,"scene":scene}))?;
    let len: usize = resp.header("Content-Length").unwrap().parse()?;
    let mut bytes: Vec<u8> = Vec::with_capacity(len);
    resp.into_reader()
        .take(10_000_000)
        .read_to_end(&mut bytes)?;
    Ok(bytes)
}

pub fn get_rand_str(len: usize) -> String {
    let str = Alphanumeric.sample_string(&mut rand::thread_rng(), len);
    str
}
